/*
 * Licensed to the Apache Software Foundation (ASF) under one or more contributor license
 * agreements. See the NOTICE file distributed with this work for additional information regarding
 * copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance with the License. You may obtain a
 * copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */

package org.apache.maven.lifecycle.internal.stub;

import org.apache.maven.execution.AbstractExecutionListener;
import org.apache.maven.execution.DefaultMavenExecutionRequest;
import org.apache.maven.execution.DefaultMavenExecutionResult;
import org.apache.maven.execution.MavenExecutionRequest;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.execution.ProjectDependencyGraph;
import org.apache.maven.lifecycle.LifecycleNotFoundException;
import org.apache.maven.lifecycle.LifecyclePhaseNotFoundException;
import org.apache.maven.lifecycle.internal.GoalTask;
import org.apache.maven.lifecycle.internal.ProjectBuildList;
import org.apache.maven.lifecycle.internal.ProjectSegment;
import org.apache.maven.lifecycle.internal.TaskSegment;
import org.apache.maven.plugin.InvalidPluginDescriptorException;
import org.apache.maven.plugin.MojoNotFoundException;
import org.apache.maven.plugin.PluginDescriptorParsingException;
import org.apache.maven.plugin.PluginNotFoundException;
import org.apache.maven.plugin.PluginResolutionException;
import org.apache.maven.plugin.prefix.NoPluginFoundForPrefixException;
import org.apache.maven.plugin.version.PluginVersionResolutionException;
import org.apache.maven.project.MavenProject;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * A stub dependency graph that is custom made for testing concurrent build graph evaluations.
 * <p/>
 * Implements a graph as follows: A has no dependencies B depends on A C depends on A X depends on B
 * & C Y depends on B Z depends on C
 *
 * @author Kristian Rosenvold
 */
public class ProjectDependencyGraphStub implements ProjectDependencyGraph {
  public static final MavenProject A = new MavenProject();

  public static final MavenProject B = new MavenProject();

  public static final MavenProject C = new MavenProject();

  public static final MavenProject X = new MavenProject();

  public static final MavenProject Y = new MavenProject();

  public static final MavenProject Z = new MavenProject();

  public static final MavenProject UNKNOWN = new MavenProject();

  static {
    A.setArtifactId("A");
    B.setArtifactId("B");
    C.setArtifactId("C");
    X.setArtifactId("X");
    Y.setArtifactId("Y");
    Z.setArtifactId("Z");
  }

  // This should probably be moved to a separate stub

  public static ProjectBuildList getProjectBuildList(MavenSession session)
      throws InvalidPluginDescriptorException, PluginVersionResolutionException, PluginDescriptorParsingException,
      NoPluginFoundForPrefixException, MojoNotFoundException, PluginNotFoundException, PluginResolutionException,
      LifecyclePhaseNotFoundException, LifecycleNotFoundException {
    final List<ProjectSegment> list = getProjectBuilds(session);
    return new ProjectBuildList(list);

  }

  public static List<ProjectSegment> getProjectBuilds(MavenSession session)
      throws InvalidPluginDescriptorException, PluginVersionResolutionException, PluginDescriptorParsingException,
      NoPluginFoundForPrefixException, PluginNotFoundException, MojoNotFoundException, PluginResolutionException,
      LifecyclePhaseNotFoundException, LifecycleNotFoundException {
    List<ProjectSegment> projectBuilds = new ArrayList<ProjectSegment>();

    TaskSegment segment = createTaskSegment();
    projectBuilds.add(createProjectBuild(A, session, segment));
    projectBuilds.add(createProjectBuild(B, session, segment));
    projectBuilds.add(createProjectBuild(C, session, segment));
    projectBuilds.add(createProjectBuild(X, session, segment));
    projectBuilds.add(createProjectBuild(Y, session, segment));
    projectBuilds.add(createProjectBuild(Z, session, segment));
    return projectBuilds;
  }

  private static ProjectSegment createProjectBuild(MavenProject project, MavenSession session, TaskSegment taskSegment)
      throws InvalidPluginDescriptorException, PluginVersionResolutionException, PluginDescriptorParsingException,
      NoPluginFoundForPrefixException, MojoNotFoundException, PluginNotFoundException, PluginResolutionException,
      LifecyclePhaseNotFoundException, LifecycleNotFoundException {
    final MavenSession session1 = session.clone();
    return new ProjectSegment(project, taskSegment, session1);
  }

  private static TaskSegment createTaskSegment() {
    TaskSegment result = new TaskSegment(false);
    result.getTasks().add(new GoalTask("t1"));
    result.getTasks().add(new GoalTask("t2"));
    return result;
  }

  class Dependency {
    MavenProject dependant;

    MavenProject dependency;

    Dependency(MavenProject dependant, MavenProject dependency) {
      this.dependant = dependant;
      this.dependency = dependency;
    }

    void addIfDownstream(MavenProject mavenProject, List<MavenProject> result) {
      if (dependency == mavenProject) {
        result.add(dependant);
      }
    }

    void addIfUpstreamOf(MavenProject mavenProject, List<MavenProject> result) {
      if (dependant == mavenProject) {
        result.add(dependency); // All projects are the statics from this class
      }
    }
  }

  private List<Dependency> getDependencies() {
    List<Dependency> dependencies = new ArrayList<Dependency>();
    dependencies.add(new Dependency(B, A));
    dependencies.add(new Dependency(C, A));
    dependencies.add(new Dependency(X, B));
    dependencies.add(new Dependency(X, C));
    dependencies.add(new Dependency(Y, B));
    dependencies.add(new Dependency(Z, C));
    return dependencies;
  }

  public List<MavenProject> getSortedProjects() {
    return Arrays.asList(A, B, C, X, Y, Z); // I'm not entirely sure about the order but this shold
                                            // do...
  }

  public List<MavenProject> getDownstreamProjects(MavenProject project, boolean transitive) {
    if (transitive) {
      throw new RuntimeException("Not implemented yet");
    }
    List<MavenProject> result = new ArrayList<MavenProject>();
    for (Dependency dependency : getDependencies()) {
      dependency.addIfDownstream(project, result);
    }
    return result;
  }

  public List<MavenProject> getUpstreamProjects(MavenProject project, boolean transitive) {
    /*
     * if ( transitive ) { throw new RuntimeException( "Not implemented yet" ); }
     */
    List<MavenProject> result = new ArrayList<MavenProject>();
    final List<Dependency> dependencies = getDependencies();
    for (Dependency dependency : dependencies) {
      dependency.addIfUpstreamOf(project, result);
    }
    return result;
  }

  public static MavenSession getMavenSession(MavenProject mavenProject) {
    final MavenSession session = getMavenSession();
    session.setCurrentProject(mavenProject);
    return session;
  }

  public static MavenSession getMavenSession() {
    final DefaultMavenExecutionResult defaultMavenExecutionResult = new DefaultMavenExecutionResult();
    MavenExecutionRequest mavenExecutionRequest = new DefaultMavenExecutionRequest();
    mavenExecutionRequest.setExecutionListener(new AbstractExecutionListener());
    mavenExecutionRequest.setGoals(Arrays.asList("clean", "aggr", "install"));
    final MavenSession session = new MavenSession(null, null, mavenExecutionRequest, defaultMavenExecutionResult);
    final ProjectDependencyGraphStub dependencyGraphStub = new ProjectDependencyGraphStub();
    session.setProjectDependencyGraph(dependencyGraphStub);
    session.setProjects(dependencyGraphStub.getSortedProjects());
    return session;
  }

}
